/**
 * @name 流程管理服务
 * @des 流程中有节点和边两种结合，由于节点后续会有很多运算，所以暂时采用 DB 库处理节点
 */

import { Injectable } from "@angular/core";
import * as _ from "lodash";
import { Subject } from "rxjs";
import { Pnode, PnodeView } from "src/app/@types/pnode";
const DB = require("@fengqiaogang/dblist");

@Injectable({
    providedIn: "root",
})
export class ProcessService {
    /** 数据库 */
    DB: any;
    /** 渲染的节点集 */
    renderPnodes: Pnode[];
    /** 装饰后的节点集 */
    nodes: Map<string | number, Pnode> = new Map();

    /** 标识符起始下标 */
    _UUIDIndex = 1;

    PnodeView: typeof PnodeView = PnodeView;

    /** 边集合 */
    edgesSubject = new Subject();

    constructor() {}

    /** 初始化节点 */
    initNodes(nodes: Pnode[]) {
        this.renderPnodes = nodes;
        this.DB = new DB(nodes);
        this.nodes = this.DB.data;
        this.updatePedges();
    }

    /** 更新边 */
    updatePedges() {
        const temp = [];
        this.nodes.forEach((value, key) => {
            const parent = this.DB.select({ id: key });
            if (parent.length > 0) {
                const { x: px, y: py } = parent[0];
                value.forEach((v, k) => {
                    const { x, y } = v;

                    const rotate = this.angle({ x, y }, { x: px, y: py });

                    // 连接点的起始坐标
                    const sourcePoints = {
                        x: px * 200 + this.PnodeView.WIDTH / 2,
                        y: py * 180 + this.PnodeView.HEIHGT / 2,
                    };

                    // 连接点的终点坐标
                    const targetPoints = {
                        x: x * 200 + this.PnodeView.WIDTH / 2,
                        y: y * 180 + this.PnodeView.HEIHGT / 2,
                    };

                    /**
                     * 当前布局，角度只能是 0,90,-90
                     */
                    let width = 0;
                    let ex, ey;
                    if (px - x !== 0) {
                        width = targetPoints.x - sourcePoints.x;
                        ex = sourcePoints.x;
                        ey = sourcePoints.y;
                    }
                    if (px - x === 0) {
                        width = targetPoints.y - sourcePoints.y;
                        ex = sourcePoints.x;
                        ey = sourcePoints.y + width;
                    }

                    const target = {
                        x: ex,
                        y: ey,
                        width,
                        rotate,
                        type: 1,
                    };
                    temp.push(target);
                });
            }
        });
        this.edgesSubject.next(temp);
        return this;
    }

    /** 插入节点 */
    insert(source: Pnode | Pnode[]) {
        const target = [].concat(source);
        this.renderPnodes.push(...target);
        this.DB.insert(target);
        return this;
    }
    /** 删除节点 */
    remove(source) {
        source.forEach(({ id }) => {
            this.renderPnodes = this.deleteById(id, this.renderPnodes);
            this.DB.remove({ id });
        });

        return this;
    }
    /** 通过ID删除 */
    deleteById(id, list) {
        for (let index = list.length - 1; index >= 0; index--) {
            if (list[index] && list[index].id === id) {
                list.splice(index, 1);
            }
        }
        return list;
    }

    /** 更新节点 */
    update(where, value) {
        this.DB.update(where, value);
        return this;
    }
    /** 求角度 */
    angle(start, end) {
        var diff_x = end.x - start.x,
            diff_y = end.y - start.y;
        // 返回角度,不是弧度
        return (360 * Math.atan(diff_y / diff_x)) / (2 * Math.PI);
    }
    /** 标识符 */
    UUid() {
        const id = `DBList_${this._UUIDIndex++}`;
        const key = String(Math.random()).slice(2);
        return `${id}_${key}`;
    }

    /**
     * 移动节点
     * @param ylevel 触发变化的y轴的层级
     * @param value 需要增加的值（可以为负）
     */
    move(ylevel: number, value = 1) {
        if (!ylevel) return this;
        const reRenderPnodes = this.renderPnodes.filter(({ y }) => y >= ylevel);
        reRenderPnodes.forEach((it) => {
            it.y = it.y + value;
            this.update({ id: it.id }, { y: it.y });
        });
        return this;
    }
}
